// m_article.go
package bmodel

import (
	"fmt"
	// "regexp"
	"strconv"
	"time"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

/*
 * count the number of articles
 * para: isPublished bool
 * return int, error
 *
 * if isPublished == true, count only these published articles
 */
func Countarticles(isPublished bool) (int, error) {
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		panic(dberr)
		return -1, fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}

	var articles []Article
	var countSize int
	if !isPublished {
		tx := db.Find(&articles)
		if tx.Error == nil {
			tx = tx.Count(&countSize)
		}
		return countSize, nil
	} else {
		tx := db.Where(&Article{State: 1}).Find(&articles)
		if tx.Error == nil {
			tx = tx.Count(&countSize)
		}
		return countSize, nil
	}
}

/*
 * count the number of articles
 * para: isPublished bool, blockid int
 * return int, error
 *
 * if isPublished == true, count only these published articles
 */
func CountarticlesByBlockId(isPublished bool, blockid int) (int, error) {
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		panic(dberr)
		return -1, fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}

	var articles []Article
	var countSize int
	if !isPublished {
		tx := db.Find(&articles)
		if tx.Error == nil {
			tx = tx.Count(&countSize)
		}
		return countSize, nil
	} else {
		tx := db.Where(&Article{State: 1, Blockid: blockid}).Find(&articles)
		if tx.Error == nil {
			tx = tx.Count(&countSize)
		}
		return countSize, nil
	}
}

/*
 * check if article occurs by articleid
 * para: articleid int
 * return: error
 */
func CheckarticleIsExistByProblemid(articleid int) error {
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		return fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}
	var articles []Article
	db.Where(&Article{Id: articleid}).Find(&articles)

	if len(articles) > 0 {
		return nil
	} else {
		return fmt.Errorf(ERROR_ARTICLE_NOT_FOUND)
	}
}

/*
 * get blockname of article by id indirectedly by blockid
 * para: id int
 * return string, error
 */
func GetblockNameOfArticleByIdIndirectedlyByBlockid(id int) (string, error) {
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	blockid := -1
	if dberr != nil {
		return "", fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}

	var articles []Article
	if db.Select("blockid").Where(&Article{Id: id}).Find(&articles); len(articles) == 1 {
		for _, v := range articles {
			blockid = v.Id
		}
		return GetblockNameById(blockid)
	} else {
		return "", fmt.Errorf(ERROR_ARTICLE_ID)
	}
}

/*
 * get all data of article by its ID
 * para: id int
 * return map[string]string, error
 */
func GetarticleById(id int) (map[string]string, error) {
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	res := make(map[string]string)
	if dberr != nil {
		panic(dberr)
		return res, fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}

	var articles []Article
	if db.Where(&Article{Id: id}).Find(&articles); len(articles) == 1 {
		for _, v := range articles {
			res["Id"] = strconv.Itoa(v.Id)
			res["Blockid"] = strconv.Itoa(v.Blockid)
			res["Priority"] = strconv.Itoa(v.Priority)
			res["Title"] = v.Title
			res["Keywords"] = v.Keywords
			res["Brief"] = v.Brief
			res["Content"] = v.Content
			res["Type"] = strconv.Itoa(v.Type)
			res["Remark"] = v.Remark
			res["State"] = strconv.Itoa(v.State)

			res["Addtime"] = v.Addtime.Format(time.RFC1123)
			res["Edittime"] = v.Edittime.Format(time.RFC1123)

			res["ext_blockname"], _ = GetblockNameById(v.Blockid) //do not check the return error
		}
		return res, nil
	} else {
		return res, fmt.Errorf(ERROR_ARTICLE_ID)
	}
}

/*
 * get all data of articles by limit: pageindex and num of articles of one page
 * para pageindex, numofpage int
 * return []map[string]string, error
 */
func GetarticlesByPage(pageindex, numofpage int) ([]map[string]string, error) {
	res := make([]map[string]string, 0)
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		panic(dberr)
		return res, fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}

	//numofpage check if valid
	if (numofpage > MAX_NUMBER_OF_INFO_IN_ONE_PAGE) || (numofpage <= MIN_NUMBER_OF_INFO_IN_ONE_PAGE) {
		res := make([]map[string]string, 1)
		return res, fmt.Errorf(ERROR_NUMOFPAGE_OUT_OF_RANGE)
	}

	var articles []Article
	if db.Offset((pageindex - 1) * numofpage).Limit(numofpage).
		Order("Priority ASC, Addtime DESC").
		Find(&articles); len(articles) > 0 {
		for _, v := range articles {
			tempmap := make(map[string]string)
			tempmap["Id"] = strconv.Itoa(v.Id)
			tempmap["Blockid"] = strconv.Itoa(v.Blockid)
			tempmap["Priority"] = strconv.Itoa(v.Priority)
			tempmap["Title"] = v.Title
			tempmap["Keywords"] = v.Keywords
			tempmap["Brief"] = v.Brief
			tempmap["Content"] = v.Content
			tempmap["Type"] = strconv.Itoa(v.Type)
			tempmap["Remark"] = v.Remark
			tempmap["State"] = strconv.Itoa(v.State)

			tempmap["Addtime"] = v.Addtime.Format("2006-01-02 15:04:05")   //.Format(time.RFC1123)
			tempmap["Edittime"] = v.Edittime.Format("2006-01-02 15:04:05") //.Format(time.RFC1123)

			tempmap["ext_blockname"], _ = GetblockNameById(v.Blockid) //do not check the return error

			res = append(res, tempmap)
		}
		return res, nil
	} else {
		return res, nil
	}
}

/*
 * get all data of articles by limit: pageindex and num of articles of one page and state=release
 * para pageindex, numofpage int
 * return []map[string]string, error
 */
func GetarticlesByPageAndState(pageindex, numofpage int) ([]map[string]string, error) {
	res := make([]map[string]string, 0)
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		panic(dberr)
		return res, fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}

	//numofpage check if valid
	if (numofpage > MAX_NUMBER_OF_INFO_IN_ONE_PAGE) || (numofpage <= MIN_NUMBER_OF_INFO_IN_ONE_PAGE) {
		res := make([]map[string]string, 1)
		return res, fmt.Errorf(ERROR_NUMOFPAGE_OUT_OF_RANGE)
	}

	var articles []Article
	if db.Offset((pageindex - 1) * numofpage).Limit(numofpage).Where(&Article{State: 1}).
		Order("Priority ASC, Addtime DESC").
		Find(&articles); len(articles) > 0 {
		for _, v := range articles {
			tempmap := make(map[string]string)
			tempmap["Id"] = strconv.Itoa(v.Id)
			tempmap["Blockid"] = strconv.Itoa(v.Blockid)
			tempmap["Priority"] = strconv.Itoa(v.Priority)
			tempmap["Title"] = v.Title
			tempmap["Keywords"] = v.Keywords
			tempmap["Brief"] = v.Brief
			tempmap["Content"] = v.Content
			tempmap["Type"] = strconv.Itoa(v.Type)
			tempmap["Remark"] = v.Remark
			tempmap["State"] = strconv.Itoa(v.State)

			tempmap["Addtime"] = v.Addtime.Format("2006-01-02 15:04:05")   //.Format(time.RFC1123)
			tempmap["Edittime"] = v.Edittime.Format("2006-01-02 15:04:05") //.Format(time.RFC1123)

			tempmap["ext_blockname"], _ = GetblockNameById(v.Blockid) //do not check the return error

			res = append(res, tempmap)
		}
		return res, nil
	} else {
		return res, nil
	}
}

/*
 * get all data of articles by limit: blockid, pageindex, num of articles of one page and state=release
 * para blockid, pageindex, numofpage int
 * return []map[string]string, error
 */
func GetarticlesByBlockidAndPageAndState(blockid, pageindex, numofpage int) ([]map[string]string, error) {
	res := make([]map[string]string, 0)
	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		panic(dberr)
		return res, fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}

	//numofpage check if valid
	if (numofpage > MAX_NUMBER_OF_INFO_IN_ONE_PAGE) || (numofpage <= MIN_NUMBER_OF_INFO_IN_ONE_PAGE) {
		res := make([]map[string]string, 1)
		return res, fmt.Errorf(ERROR_NUMOFPAGE_OUT_OF_RANGE)
	}

	var articles []Article
	if db.Where(&Article{Blockid: blockid, State: 1}).Offset((pageindex - 1) * numofpage).Limit(numofpage).
		Order("Priority ASC, Addtime DESC").
		Find(&articles); len(articles) > 0 {
		for _, v := range articles {
			tempmap := make(map[string]string)
			tempmap["Id"] = strconv.Itoa(v.Id)
			tempmap["Blockid"] = strconv.Itoa(v.Blockid)
			tempmap["Priority"] = strconv.Itoa(v.Priority)
			tempmap["Title"] = v.Title
			tempmap["Keywords"] = v.Keywords
			tempmap["Brief"] = v.Brief
			tempmap["Content"] = v.Content
			tempmap["Type"] = strconv.Itoa(v.Type)
			tempmap["Remark"] = v.Remark
			tempmap["State"] = strconv.Itoa(v.State)

			tempmap["Addtime"] = v.Addtime.Format("2006-01-02 15:04:05")   //.Format(time.RFC1123)
			tempmap["Edittime"] = v.Edittime.Format("2006-01-02 15:04:05") //.Format(time.RFC1123)

			tempmap["ext_blockname"], _ = GetblockNameById(v.Blockid) //do not check the return error

			res = append(res, tempmap)
		}
		return res, nil
	} else {
		return res, nil
	}
}

/*
 * add article
 * para: articleBlockId, articlePriority, articleTitle,
	articleKeywords, articleBrief, articleContent,
	articleType, articleRemark, articleState
 * return error
*/
func Addarticle(articleBlockId, articlePriority, articleTitle,
	articleKeywords, articleBrief, articleContent,
	articleType, articleRemark, articleState string) error {

	var articleMap = map[string]string{
		"Blockid":  articleBlockId,
		"Priority": articlePriority,
		"Title":    articleTitle,
		"Content":  articleContent,
		"Keywords": articleKeywords,
		"Brief":    articleBrief,
		"Type":     articleType,
		"Remark":   articleRemark,
		"State":    articleState,

		"Reserve": "default",
	}

	var fdata = make(map[string]interface{}, 0)
	if errf := firstFilter("article", articleMap, &fdata); errf != nil {
		return errf
	}

	pStruct := Article{}
	if errs := secondFilter("article", fdata, &pStruct); errs != nil {
		return errs
	}

	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		return fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}
	if errc := db.Create(&pStruct).Error; errc != nil {
		return fmt.Errorf(ERROR_ARTICLE_CANNOT_BE_CREATED)
	} else {
		return nil
	}
}

/*
 * edit article by its ID
 * para: id int, and nine strings
 *     articleBlockId, articlePriority, articleTitle,
	articleKeywords, articleBrief, articleContent,
	articleType, articleRemark, articleState string
 * return error
*/
func EditarticlesById(id int, articleBlockId, articlePriority, articleTitle,
	articleKeywords, articleBrief, articleContent,
	articleType, articleRemark, articleState string) error {

	var articleMap = map[string]string{
		"Blockid":  articleBlockId,
		"Priority": articlePriority,
		"Title":    articleTitle,
		"Content":  articleContent,
		"Keywords": articleKeywords,
		"Brief":    articleBrief,
		"Type":     articleType,
		"Remark":   articleRemark,
		"State":    articleState,

		"Reserve": "default",
	}

	var fdata = make(map[string]interface{}, 0)
	if errf := firstFilter("article", articleMap, &fdata); errf != nil {
		return errf
	}

	db, dberr := gorm.Open("mysql", DB_CONNECT_STR)
	defer db.Close()
	if dberr != nil {
		return fmt.Errorf(ERROR_CANNOT_OPEN_DB)
	}
	var articles []Article

	if db.Where(&Article{Id: id}).Find(&articles); len(articles) == 1 {
		db.Where(&Article{Id: id}).Model(&articles).Updates(fdata)
		return nil
	} else {
		return fmt.Errorf(ERROR_ARTICLE_NOT_FOUND)
	}
}

/*
 * check function for article:blockid
 * para: s string(raw data)
 * return int, error
 * check if target block exists
 */
func checkIfArticleBlockidValid(s string) (int, error) {
	if sInt, erro := strconv.Atoi(s); erro != nil {
		return 0, erro
	} else {
		if errc := checkIfSubLevelBlock(sInt); errc != nil {
			return 0, errc
		} else {
			return sInt, nil
		}
	}
}

/*
 * get article default settings
 * para: none
 * return map[string]string
 */
func GetarticleDefaultSettings() map[string]map[string]map[string]string {
	return ARTICLE_CHOOSES_TABLE
}

func TEST_bModel_article() {
	articles, aerr := GetarticlesByPage(1, 3)
	fmt.Println(articles)
	fmt.Println(aerr)

	articles2, aerr2 := GetarticlesByPageAndState(1, 3)
	fmt.Println(articles2)
	fmt.Println(aerr2)
}
